#pragma once

#include "Epoll.hpp"
#include "Sock.hpp"
#include "log.hpp"
#include "Protocol.hpp"
#include <functional>
#include <unordered_map>
#include <unistd.h>
#include <vector>
#include <string>
#include <cassert>

class Connection;
class TcpServer;
using func_t = std::function<void(Connection *)>;
using callback_t = std::function<void(Connection *, std::string &requset)>;

class Connection
{
public:
    Connection(int sock = -1)
        : _sock(sock), _tsvr(nullptr)
    {
    }
    ~Connection()
    {
    }

    void SetCallBack(func_t recv, func_t send, func_t except)
    {
        _recv_cb = recv;
        _send_cb = send;
        _except_cb = except;
    }

public:
    int _sock;
    func_t _recv_cb;
    func_t _send_cb;
    func_t _except_cb;
    std::string _inbuffer;
    std::string _outbuffer;
    TcpServer *_tsvr;
};

class TcpServer
{
    const static int gport;
    const static int gnum;

public:
    TcpServer(int port = 8080)
        : _port(port), _revs_num(gnum)
    {
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);

        _poll.CreateEpoll();
        AddConnction(_listensock, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        _revs = new struct epoll_event[_revs_num];
    }
    ~TcpServer()
    {
        if (_listensock >= 0)
            close(_listensock);
        if (_revs)
            delete[] _revs;
    }
    void AddConnction(int sock, func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        Sock::SetNonBlock(sock);

        Connection *conn = new Connection(sock);
        conn->_recv_cb = recv_cb;
        conn->_send_cb = send_cb;
        conn->_except_cb = except_cb;
        conn->_tsvr = this;

        _poll.AddSockToEpoll(sock, EPOLLIN | EPOLLET);
        _connections.insert(std::make_pair(sock, conn));
    }
    void Accepter(Connection *conn)
    {
        while (true)
        {
            std::string clientip;
            uint16_t clientport;

            int accept_errno = 0;
            int sock = Sock::Accept(_listensock, &clientip, &clientport, &accept_errno);
            if (sock < 0)
            {
                if (accept_errno == EAGAIN || accept_errno == EWOULDBLOCK)
                    break;
                else if (accept_errno == EINTR)
                    continue;
                else
                {
                    logMessage(WARNING, "accept error, %d : %s", accept_errno, strerror(accept_errno));
                    break;
                }
            }
            if (sock >= 0)
            {
                AddConnction(sock, std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                             std::bind(&TcpServer::Sender, this, std::placeholders::_1), std::bind(&TcpServer::Excepter, this, std::placeholders::_1));
            }
            logMessage(DEBUG, "accept client %s:%d success, add to epoll&&TcpServer success, sock: %d",
                       clientip.c_str(), clientport, sock);
        }
    }

    void Recver(Connection *conn)
    {
        const int num = 1024;
        bool err = false;
        while (true)
        {
            char buffer[num];
            ssize_t n = recv(conn->_sock, buffer, sizeof(buffer) - 1, 0);
            if (n < 0)
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    logMessage(ERROR, "recv error, %d : %s", errno, strerror(errno));
                    conn->_except_cb(conn);
                    err = true;
                    break;
                }
            }
            else if (n == 0)
            {
                logMessage(DEBUG, "client[%d] quit, server close [%d]", conn->_sock, conn->_sock);
                conn->_except_cb(conn);
                err = true;
                break;
            }
            else
            {
                buffer[n] = 0;
                conn->_inbuffer += buffer;
            }
        }
        logMessage(DEBUG, "conn->_inbuffer[sock: %d]: %s", conn->_sock, conn->_inbuffer.c_str());
        if (!err)
        {
            std::vector<std::string> messages;
            SpliteMessage(conn->_inbuffer, &messages);
            for (auto &msg : messages)
            {
                _cb(conn, msg);
            }
        }
    }
    void Sender(Connection *conn)
    {
        while (true)
        {
            ssize_t n = send(conn->_sock, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
            if (n > 0)
            {
                conn->_outbuffer.erase(0, n);
                if (conn->_outbuffer.empty())
                    break;
            }
            else
            {
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if(errno == EINTR)
                    continue;
                else
                {
                    logMessage(ERROR, "send error, %d : %s", errno, strerror(errno));
                    conn->_except_cb(conn);
                    break;
                }
            }
        }
        if(conn->_inbuffer.empty())
            EnableReadWrite(conn,true,false);
        else
            EnableReadWrite(conn,true,true);
    }
    void Excepter(Connection *conn)
    {
        if(!IsConnectionExits(conn->_sock))
            return;
        else
        {
            bool res = _poll.DelFromEpoll(conn->_sock);
            assert(res);
            _connections.erase(conn->_sock);
            delete conn;
            logMessage(DEBUG, "Excepter 回收完毕，所有的异常情况"); 
        }
    }

    void EnableReadWrite(Connection* conn,bool readable,bool writeable)
    {
        uint32_t events = (readable?EPOLLIN:0)|(writeable?EPOLLOUT:0);
        bool res = _poll.CtlEpoll(conn->_sock,events);
        assert(res);
    }
    void Dispather(callback_t cb)
    {
        _cb = cb;
        while (true)
        {
            LoopOnce();
        }
    }

    void LoopOnce()
    {
        int n = _poll.WaitEpoll(_revs, _revs_num);
        for (int i = 0; i < n; i++)
        {
            int sock = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;

            if (revents & EPOLLERR)
                revents |= (EPOLLIN | EPOLLOUT);
            if (revents & EPOLLHUP)
                revents |= (EPOLLIN | EPOLLOUT);

            if (revents & EPOLLIN)
            {
                if (IsConnectionExits(sock) && _connections[sock]->_recv_cb != nullptr)
                    _connections[sock]->_recv_cb(_connections[sock]);
            }
            if (revents & EPOLLOUT)
            {
                if (IsConnectionExits(sock) && _connections[sock]->_send_cb != nullptr)
                    _connections[sock]->_send_cb(_connections[sock]);
            }
        }
    }

    bool IsConnectionExits(int sock)
    {
        auto iter = _connections.find(sock);
        if (iter == _connections.end())
            return false;
        else
            return true;
    }

private:
    int _listensock;
    int _port;

    std::unordered_map<int, Connection *> _connections;
    Epoll _poll;
    struct epoll_event *_revs;
    int _revs_num;

    callback_t _cb;
};
const int TcpServer::gport = 8080;
const int TcpServer::gnum = 128;